<template>
  <div class="bluetooth">
    <div v-if="loading" class="overlay">
      <div class="loading-box">🔄 {{ loadingMessage }}</div>
    </div>
    <div class="handle-box">
      <div class="btn-box">
        <button @click="addDevice">添加蓝牙设备</button>
        <button style="background: steelblue" @click="sensorOpen">
          校准传感器
        </button>
        <button style="background: sienna" @click="rockerOpen">校准摇杆</button>
        <button style="background: #ff4500" @click="motorOpen">
          测试马达震动
        </button>
      </div>
      <div class="input-box">
        <el-input
          placeholder="请输入质检员账号"
          prefix-icon="el-icon-user-solid"
          v-model="qualityTestingAccount"
        >
        </el-input>
        <el-input
          placeholder="请输入质检码（密码）"
          prefix-icon="el-icon-mouse"
          v-model="qualityTestingCode"
        >
        </el-input>
        <el-button type="primary" class="add-btn" @click="sumbitQualified"
          >合格上传</el-button
        >
      </div>
      <div class="operate-box">
        <div class="seleted" @click="changeAllCheck">
          <p>
            <img
              :src="
                isAllCheck
                  ? require('../assets/checked.png')
                  : require('../assets/check.png')
              "
              alt=""
            />
          </p>
          全选
        </div>
        <el-button class="reset-btn" @click="clearAll">一键清空</el-button>
      </div>
    </div>
    <el-divider>温馨提示：设备连接后，请先校准摇杆再做摇杆检测！</el-divider>
    <div class="device-block">
      <el-card
        class="box-card"
        v-for="(d, i) in devices"
        :key="i"
        :class="{
          'blink-error': d.isSensor === 'fail' || d.isRocker === 'fail',
          'device-success': d.passed,
        }"
      >
        <p class="remove-box" @click="delDevice(i)">
          <i class="el-icon-remove"></i>
        </p>
        <div slot="header" class="clearfix">
          <div class="seleted" @click="changeCheck(i)">
            <p>
              <img
                :src="
                  d.isSeleted
                    ? require('../assets/checked.png')
                    : require('../assets/check.png')
                "
                alt=""
              />
            </p>
            <div>
              <span>{{ d.device.name || "未知设备" }}</span>
              <span class="status"
                >({{ d.connected ? "🟢 已连接" : "🔴 断开" }})</span
              >
              <el-button
                class="blue-btn"
                v-if="!d.connected && d.isReconnection"
                @click.stop="reconnectDevice(d)"
                >重连</el-button
              >
            </div>
          </div>
          <div class="battery-box">
            <!-- 电池外壳 -->
            <div class="battery-shell">
              <!-- 电量填充 -->
              <div
                class="battery-level"
                :style="{
                  width: d.batteryPercent + '%',
                  backgroundColor: d.batteryColor,
                }"
              ></div>
            </div>
            <!-- 电池触点头 -->
            <div class="battery-head"></div>
            <span class="battery-text">{{ d.batteryPercent }}</span>
          </div>
        </div>
        <div class="top">
          <div>
            <div>
              L
              <div
                class="btn"
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'L') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
            </div>
            <div>
              ZL
              <div
                class="btn"
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'ZL') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
            </div>
          </div>
          <div>
            <div>
              ZR
              <div
                class="btn"
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'ZR') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
            </div>
            <div>
              R
              <div
                class="btn"
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'R') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
            </div>
          </div>
        </div>
        <div class="center">
          <div class="rocker">
            <div
              class="circle"
              :style="{ background: d.direction.y > 0 ? '#27FF2C' : '#323232' }"
            ></div>
            <div class="item-center">
              <div
                class="circle"
                :style="{
                  background: d.direction.x < 0 ? '#27FF2C' : '#323232',
                }"
              ></div>
              <div class="rocker-btn">
                <img src="../assets/direction.png" alt="" />
                <div
                  class="circle"
                  :style="{ background: d.gyroBtn ? '#27FF2C' : '#323232' }"
                ></div>
              </div>
              <div
                class="circle"
                :style="{
                  background: d.direction.x > 0 ? '#27FF2C' : '#323232',
                }"
              ></div>
            </div>
            <div
              class="circle"
              :style="{ background: d.direction.y < 0 ? '#27FF2C' : '#323232' }"
            ></div>
          </div>
          <div class="gyroscope">
            <div>
              <div v-if="!d.keyChecked" class="loading-dots">
                <span class="dot"></span>
                <span class="dot"></span>
                <span class="dot"></span>
                <p>按键检测中</p>
              </div>
              <div v-else class="check-success">
                <div class="check-icon">✔</div>
                <p>按键全部正常</p>
              </div>
            </div>
            <div>
              <div v-if="!d.rockerChecked" class="loading-dots">
                <span class="dot"></span>
                <span class="dot"></span>
                <span class="dot"></span>
                <p>摇杆检测中</p>
              </div>
              <div v-else class="check-success">
                <div class="check-icon">✔</div>
                <p>摇杆检测正常</p>
              </div>
            </div>
          </div>
          <div class="button">
            <div class="circle-btn">
              X
              <div
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'X') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
            </div>
            <div class="item-center">
              <div class="circle-btn">
                Y
                <div
                  style="margin-left: 8px"
                  :style="{
                    background:
                      d.keyList.findIndex((item) => item === 'Y') !== -1
                        ? '#27FF2C'
                        : '#323232',
                  }"
                ></div>
              </div>
              <img src="../assets/direction.png" alt="" />
              <div class="circle-btn">
                <div
                  style="margin-right: 8px"
                  :style="{
                    background:
                      d.keyList.findIndex((item) => item === 'A') !== -1
                        ? '#27FF2C'
                        : '#323232',
                  }"
                ></div>
                A
              </div>
            </div>
            <div class="circle-btn">
              <div
                :style="{
                  background:
                    d.keyList.findIndex((item) => item === 'B') !== -1
                      ? '#27FF2C'
                      : '#323232',
                }"
              ></div>
              B
            </div>
          </div>
        </div>
        <el-divider></el-divider>
        <div class="calibration-box">
          <div class="calibration">
            <h2>九轴校准：</h2>
            <div>
              <p>
                <img :src="require(`../assets/${d.isSensor}.png`)" alt="" />
              </p>
              <span>{{
                d.isSensor === "success"
                  ? "校准成功"
                  : d.isSensor === "fail"
                  ? "校准失败"
                  : "等待校准"
              }}</span>
            </div>
          </div>
          <div class="calibration">
            <h2>摇杆校准：</h2>
            <div>
              <p>
                <img :src="require(`../assets/${d.isRocker}.png`)" alt="" />
              </p>
              <span>{{
                d.isRocker === "success"
                  ? "校准成功"
                  : d.isRocker === "fail"
                  ? "校准失败"
                  : "等待校准"
              }}</span>
            </div>
          </div>
        </div>
      </el-card>
    </div>
    <!-- 校准传感器的弹窗 -->
    <el-dialog
      :visible.sync="sensorVisible"
      width="30%"
      :before-close="sensorClose"
      :show-close="false"
    >
      <div slot="title" class="title">
        <p>提示</p>
        <span @click="sensorClose"><i class="el-icon-close"></i></span>
      </div>
      <div class="sensor-box">请将手柄静止放置在桌面上</div>
      <span slot="footer" class="dialog-footer">
        <el-button class="cancel-btn" @click="sensorClose">取 消</el-button>
        <el-button type="primary" class="enter-btn" @click="sumbitSensor()"
          >确 定</el-button
        >
      </span>
    </el-dialog>
    <!-- 校准摇杆的弹窗 -->
    <el-dialog
      :visible.sync="rockerVisible"
      width="30%"
      :before-close="rockerClose"
      :show-close="false"
    >
      <div slot="title" class="title">
        <p>提示</p>
        <span @click="rockerClose"><i class="el-icon-close"></i></span>
      </div>
      <div class="rocker-box">
        <p>1.请将手柄处于一个完全水平的状态</p>
        <p>2.待灯光亮起，左右上下摇动一下摇杆</p>
        <p>3.按B结束校验</p>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button class="cancel-btn" @click="rockerClose">取 消</el-button>
        <el-button type="primary" class="enter-btn" @click="sumbitRocker()"
          >确 定</el-button
        >
      </span>
    </el-dialog>
    <!-- 测试马达的弹窗 -->
    <el-dialog
      :visible.sync="motorVisible"
      width="30%"
      :before-close="motorClose"
      :show-close="false"
    >
      <div slot="title" class="title">
        <p>调节马达</p>
        <span @click="motorClose"><i class="el-icon-close"></i></span>
      </div>
      <div class="motor-box">
        <div>
          <span class="demonstration">设置震荡马达的力度（pwm）</span>
          <div>
            <el-slider v-model="power"></el-slider>
            <p>{{ power }}</p>
          </div>
        </div>
        <div>
          <span class="demonstration">设置震荡马达的时间（ms）</span>
          <div>
            <el-slider v-model="time" :max="255"></el-slider>
            <p>{{ time }}</p>
          </div>
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button class="cancel-btn" @click="motorClose">取 消</el-button>
        <el-button type="primary" class="enter-btn" @click="sumbitMotor()"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
import tools from "@/utils/tools.js";
import { addCheckDevice } from "@/api";
export default {
  data() {
    return {
      serviceUUID: "00000000-0000-1000-8000-00805f9b34fb",
      notifyUUID: "0000ffe2-0000-1000-8000-00805f9b34fb",
      isAllCheck: false,
      devices: [],
      // 传感器
      sensorVisible: false,
      // 摇杆
      rockerVisible: false,
      // 马达
      motorVisible: false,
      power: 0,
      time: 0,
      sendHex: "", // 用户输入的 hex 字符串
      sendTargetIndex: 0, // 发送目标设备索引
      loading: false,
      loadingMessage: "",
      // 选中的数量
      selectNumber: 0,
      nowNumber: 0,
      qualityTestingAccount: "", //质检员账号
      qualityTestingCode: "", //质检码
      uploading: false,
    };
  },
  async mounted() {
    this.restoreRememberedDevices();
    window.addEventListener("beforeunload", this.disconnectAll);
  },
  beforeDestroy() {
    this.disconnectAll();
    window.removeEventListener("beforeunload", this.disconnectAll);
  },

  methods: {
    async addDevice() {
      if (this.devices.length >= 20) {
        this.$message.warning("仅支持一次性检测20个设备");
        return;
      }
      try {
        const device = await navigator.bluetooth.requestDevice({
          filters: [{ namePrefix: "L05" }],
          optionalServices: [this.serviceUUID],
        });

        await this.connectDevice(device); // 分离逻辑
        this.saveRememberedDevice(device.id);
      } catch (error) {
        console.warn("用户取消或添加失败", error);
      }
    },

    async connectDevice(device) {
      this.loading = true;
      this.loadingMessage = `正在连接设备 ${device.name}...`;
      try {
        const server = await device.gatt.connect();
        const service = await server.getPrimaryService(this.serviceUUID);
        const notifyChar = await service.getCharacteristic(this.notifyUUID);

        await notifyChar.startNotifications();

        const deviceObj = await this.buildDeviceObject(
          device,
          server,
          notifyChar
        );
        this.registerNotify(deviceObj);

        this.devices.push(deviceObj);
        this.saveRememberedDevice(device.id);

        device.addEventListener("gattserverdisconnected", () => {
          deviceObj.connected = false;
          // console.warn(`[${device.name}] 连接断开，尝试重连...`);
          // this.retryReconnect(deviceObj, 3); // 尝试重连最多 3 次
          if (!deviceObj.manuallyDisconnect) {
            console.warn(`[${device.name}] 连接断开，尝试重连...`);
            this.retryReconnect(deviceObj, 3);
          } else {
            console.log(`[${device.name}] 手动断开，不执行重连`);
          }
        });

        console.log("✅ 成功连接：", device.name);
        console.log(deviceObj, "deviceObj");
        // this.loading = false;
      } catch (err) {
        console.error("❌ 连接失败：", err);
        this.$message.error(`❌ ${device.name}连接失败，请重新尝试连接。`);
        if (device.gatt?.connected) {
          device.gatt.disconnect();
          console.log("🔌 连接失败后强制断开");
        }
        // this.loading = false;
      } finally {
        // ✅ 无论连接成功或失败，此处再关闭 loading
        this.loading = false;
      }
    },
    // 自动重新连接三次
    async retryReconnect(deviceObj, maxRetry = 3) {
      let attempt = 0;

      const delay = (ms) => new Promise((res) => setTimeout(res, ms));

      while (attempt < maxRetry) {
        attempt++;
        console.log(
          `🔁 第 ${attempt} 次尝试重连 [${deviceObj.device.name}]...`
        );

        try {
          const server = await deviceObj.device.gatt.connect();
          const service = await server.getPrimaryService(this.serviceUUID);
          const characteristic = await service.getCharacteristic(
            this.notifyUUID
          );

          await characteristic.startNotifications();
          characteristic.addEventListener(
            "characteristicvaluechanged",
            (event) => this.handleNotify(event, deviceObj)
          );

          // 恢复状态
          deviceObj.connected = true;
          deviceObj.server = server;
          deviceObj.characteristic = characteristic;
          deviceObj.isReconnection = false;
          console.log(`✅ 重新连接成功: [${deviceObj.device.name}]`);
          return;
        } catch (error) {
          console.warn(`❌ 第 ${attempt} 次重连失败:`, error);
          await delay(1500); // 等待 1.5 秒再重试
        }
      }
      deviceObj.isReconnection = true;
      console.error(
        `❌ 无法重新连接设备 [${deviceObj.device.name}]，请手动重连`
      );
    },
    // 手动重新连接
    async reconnectDevice(deviceObj) {
      try {
        if (!deviceObj.device.gatt.connected) {
          console.log(`🔁 正在尝试手动重连 [${deviceObj.device.name}]...`);
          const server = await deviceObj.device.gatt.connect();
          const service = await server.getPrimaryService(this.serviceUUID);
          const characteristic = await service.getCharacteristic(
            this.notifyUUID
          );

          await characteristic.startNotifications();
          characteristic.addEventListener(
            "characteristicvaluechanged",
            (event) => this.handleNotify(event, deviceObj)
          );

          // 更新 deviceObj 状态
          deviceObj.connected = true;
          deviceObj.server = server;
          deviceObj.characteristic = characteristic;

          console.log(`✅ 手动重连成功: ${deviceObj.device.name}`);
        } else {
          console.log(`✅ 设备已连接: ${deviceObj.device.name}`);
        }
      } catch (error) {
        console.error(`❌ 手动重连失败: ${deviceObj.device.name}`, error);
        alert(`设备 [${deviceObj.device.name}] 连接失败，请确认设备开启`);
      }
    },
    async restoreRememberedDevices() {
      const stored = JSON.parse(
        localStorage.getItem("rememberedBluetoothDevices") || "[]"
      );
      if (navigator.bluetooth && navigator.bluetooth.getDevices) {
        const remembered = await navigator.bluetooth.getDevices();
        for (const d of remembered) {
          if (stored.includes(d.id)) {
            try {
              await this.connectDevice(d);
            } catch (err) {
              console.warn("恢复连接失败：", err);
            }
          }
        }
      }
    },

    buildDeviceObject(device, server, characteristic) {
      return {
        device,
        server,
        characteristic,
        hexCache: "",
        isRocker: "loading",
        isSensor: "loading",
        isReconnection: false,
        manuallyDisconnect: false,
        connected: true,
        messages: [],
        keyList: [],
        gyroBtn: false,
        direction: { x: 0, y: 0 },
        isSeleted: false,
        keyTestStatus: {
          X: false,
          Y: false,
          B: false,
          A: false,
          L: false,
          ZL: false,
          ZR: false,
          R: false,
        },
        rockerTestStatus: {
          up: false,
          down: false,
          left: false,
          right: false,
          press: false,
        },
      };
    },

    registerNotify(deviceObj) {
      deviceObj.characteristic.addEventListener(
        "characteristicvaluechanged",
        (event) => {
          this.handleNotify(event, deviceObj);
        }
      );
    },

    handleNotify(event, deviceObj) {
      const value = event.target.value;
      const hexStr = Array.from(new Uint8Array(value.buffer))
        .map((b) => b.toString(16).padStart(2, "0"))
        .join("")
        .toLowerCase();

      if (hexStr.includes("a506aa7788")) {
        // alert(`[${deviceObj.device.name}] 摇杆校验成功`);
        deviceObj.isRocker = "success";
        return;
      }
      if (hexStr.includes("a506ae7788")) {
        // alert(`[${deviceObj.device.name}] 摇杆校验失败`);
        deviceObj.isRocker = "fail";
        return;
      }
      if (hexStr.includes("a506aa5566")) {
        // alert(`[${deviceObj.device.name}] 6轴校准成功`);
        deviceObj.isSensor = "success";
        return;
      }
      if (hexStr.includes("a506ae5566")) {
        // alert(`[${deviceObj.device.name}] 6轴校准失败`);
        deviceObj.isSensor = "fail";
        return;
      }

      deviceObj.hexCache += hexStr;

      while (deviceObj.hexCache.length >= 6) {
        let found = false;
        for (let i = 0; i <= deviceObj.hexCache.length - 6; i += 2) {
          if (deviceObj.hexCache.slice(i, i + 2) === "a5") {
            const len = parseInt(deviceObj.hexCache.slice(i + 2, i + 4), 16);
            const frameLen = len * 2;
            if (deviceObj.hexCache.length - i < frameLen) break;

            const frameHex = deviceObj.hexCache.slice(i, i + frameLen);
            let sum = 0;
            for (let j = 0; j < frameLen - 2; j += 2) {
              sum += parseInt(frameHex.slice(j, j + 2), 16);
            }
            sum = sum & 0xff;
            const checkSumHex = frameHex.slice(frameLen - 2);
            const checkSum = parseInt(checkSumHex, 16);

            if (sum === checkSum) {
              const payload = frameHex.slice(6, frameLen - 2);
              this.processData(payload, deviceObj);
              deviceObj.hexCache = deviceObj.hexCache.slice(i + frameLen);
              found = true;
              break;
            } else {
              deviceObj.hexCache = deviceObj.hexCache.slice(i + 2);
              found = true;
              break;
            }
          }
        }
        if (!found) break;
      }
    },

    processData(payloadHex, deviceObj) {
      //按键
      this.processKey(payloadHex.slice(0, 2), deviceObj);
      // 处理摇杆数据
      this.processRocker(
        payloadHex.slice(2, 10),
        payloadHex.slice(64, 65),
        deviceObj
      );
      // 电量
      this.processBattery(payloadHex.slice(62, 64), deviceObj);

      // 判断检验是否合格
      this.checkDevicePassed(deviceObj);
    },
    checkDevicePassed(deviceObj) {
      if (deviceObj.passed) return;
      if (
        deviceObj.rockerChecked &&
        deviceObj.keyChecked &&
        deviceObj.isRocker === "success" &&
        deviceObj.isSensor === "success"
      ) {
        if (!deviceObj.passed) {
          deviceObj.passed = true; // 标记为合格
        }
      }
    },
    processKey(value, deviceObj) {
      const btnValue = tools.hex2int(value);
      const buttons = {
        X: 1,
        Y: 2,
        B: 4,
        A: 8,
        L: 128,
        ZL: 64,
        ZR: 16,
        R: 32,
      };
      const pressed = [];
      for (let key in buttons) {
        if ((btnValue & buttons[key]) !== 0) {
          pressed.push(key);
          // 更新测试状态
          if (deviceObj.keyTestStatus) {
            deviceObj.keyTestStatus[key] = true;
          }
        }
      }
      deviceObj.keyList = pressed;
      this.checkKeyTestComplete(deviceObj);
    },
    // isKeyTestComplete(deviceObj) {
    //   return (
    //     deviceObj.keyTestStatus &&
    //     Object.values(deviceObj.keyTestStatus).every((v) => v === true)
    //   );
    // },
    checkKeyTestComplete(deviceObj) {
      if (deviceObj.keyChecked) return;
      const allTested = Object.values(deviceObj.keyTestStatus).every((v) => v);
      if (allTested) {
        deviceObj.keyChecked = true;
      }
    },
    // 处理摇杆
    processRocker(value, key, deviceObj) {
      const x = this.parseLittleEndian16(value.slice(0, 4)); // 归一化后的 [-1, 1]
      const y = this.parseLittleEndian16(value.slice(4, 8));
      deviceObj.direction.x = x;
      deviceObj.direction.y = y;
      deviceObj.gyroBtn = tools.hex2int(key) === 8 ? true : false;
      const threshold = 0.3;
      if (deviceObj.rockerTestStatus) {
        if (y < -threshold) deviceObj.rockerTestStatus.up = true;
        if (y > threshold) deviceObj.rockerTestStatus.down = true;
        if (x < -threshold) deviceObj.rockerTestStatus.left = true;
        if (x > threshold) deviceObj.rockerTestStatus.right = true;
        deviceObj.rockerTestStatus.press = tools.hex2int(key) === 8;
      }
      this.checkRockerComplete(deviceObj);
    },
    // isCheckRockerComplete(deviceObj) {
    //   return (
    //     deviceObj.rockerTestStatus &&
    //     Object.values(deviceObj.rockerTestStatus).every((v) => v === true)
    //   );
    // },
    checkRockerComplete(deviceObj) {
      if (deviceObj.rockerChecked) return;
      const allTested = Object.values(deviceObj.rockerTestStatus).every(
        (v) => v
      );
      if (allTested) {
        deviceObj.rockerChecked = true;
      }
    },
    // 处理电量
    processBattery(value, deviceObj) {
      const battery = tools.hex2int(value);
      deviceObj.batteryPercent = battery;
      deviceObj.batteryColor =
        battery >= 60 ? "#46a149" : battery >= 20 ? "#f0b606" : "#F44336";
    },
    parseLittleEndian16(hexStr) {
      if (hexStr.length !== 4) throw new Error("必须是4位hex字符串");
      const reordered = hexStr.slice(2, 4) + hexStr.slice(0, 2);
      let value = parseInt(reordered, 16);
      if (value > 0x7fff) value -= 0x10000; // 有符号转换
      const normalized = Math.max(-1, Math.min(1, value / 32767));
      return Number(normalized.toFixed(4));
    },
    saveRememberedDevice(id) {
      const stored = JSON.parse(
        localStorage.getItem("rememberedBluetoothDevices") || "[]"
      );
      if (!stored.includes(id)) {
        stored.push(id);
        localStorage.setItem(
          "rememberedBluetoothDevices",
          JSON.stringify(stored)
        );
      }
    },
    disconnectAll() {
      this.devices.forEach((d) => {
        if (d.device?.gatt?.connected) {
          d.device.gatt.disconnect();
        }
      });
    },
    // 删除连接蓝牙设备
    delDevice(index) {
      this.$confirm("即将为您移除这个设备", "提示", {
        confirmButtonText: "确定",
        showCancelButton: false,
        type: "warning",
        showClose: false,
      })
        .then(async () => {
          if (index !== -1) {
            const deviceObj = this.devices[index];
            deviceObj.manuallyDisconnect = true;
            // 尝试断开蓝牙连接
            try {
              if (
                deviceObj.device &&
                deviceObj.device.gatt &&
                deviceObj.device.gatt.connected
              ) {
                await deviceObj.device.gatt.disconnect();
              }
            } catch (disconnectErr) {
              console.warn("⚠️ 断开时出错：", disconnectErr);
            }

            // 移除设备
            this.devices.splice(index, 1);
          } else {
            this.$message.warning("未在设备列表中找到");
          }
        })
        .catch(() => {});
    },
    clearAll() {
      this.$confirm("即将为您移除所有设备", "提示", {
        confirmButtonText: "确定",
        showCancelButton: false,
        type: "warning",
        showClose: false,
      })
        .then(async () => {
          // 先尝试断开所有连接
          this.devices.forEach((d) => {
            d.manuallyDisconnect = true;
            try {
              if (d.device?.gatt?.connected) {
                d.device.gatt.disconnect();
                console.log(`🔌 已断开设备: ${d.device.name}`);
              }
            } catch (err) {
              console.warn(`❌ 断开设备出错: ${d.device.name}`, err);
            }
          });

          // 清空设备列表
          this.devices = [];
          // 可选：清除已保存的本地记录
          localStorage.removeItem("rememberedBluetoothDevices");

          this.$message.success("已清空所有设备");
        })
        .catch(() => {});
    },
    // 切换选中状态
    changeCheck(index) {
      // if (this.devices[index].passed) return;
      this.devices[index].isSeleted = !this.devices[index].isSeleted;
      this.devices.map((el) => {
        if (!el.isSeleted) {
          this.isAllCheck = false;
        }
      });
    },
    // 全选/全部选
    changeAllCheck() {
      this.isAllCheck = !this.isAllCheck;
      this.devices = this.devices.map((el) => {
        // if (!el.passed) {
        el.isSeleted = this.isAllCheck;
        // }
        return el;
      });
    },
    // 传感器校验
    sensorOpen() {
      const arr = this.devices.filter((el) => el.isSeleted && !el.passed);
      if (arr.length === 0) {
        this.$message.error("请选择需要校准的手柄");
        return;
      }
      this.selectNumber = arr.length;
      this.sensorVisible = true;
    },
    sensorClose() {
      this.sensorVisible = false;
      this.nowNumber = 0;
    },
    sumbitSensor() {
      let frame = [0xa5, 0x06, 0xbb, 0x55, 0x66];
      const checksum = frame.reduce((sum, val) => sum + val, 0) & 0xff;
      frame.push(checksum);
      const sendHex = frame
        .map((val) => val.toString(16).padStart(2, "0"))
        .join("");
      console.log("发送 Hex：", sendHex);
      const buffer = new Uint8Array(frame).buffer;
      this.devices.forEach((deviceObj, index) => {
        if (deviceObj.connected && deviceObj.isSeleted && !deviceObj.passed) {
          this.sendDataToDevice(deviceObj, buffer, "sensorClose");
        } else {
          console.warn(
            `❌ 设备 ${deviceObj.device.name || index} 未连接，跳过`
          );
        }
      });
    },
    // 摇杆校验
    rockerOpen() {
      const arr = this.devices.filter((el) => el.isSeleted && !el.passed);
      if (arr.length === 0) {
        this.$message.error("请选择需要校准的手柄");
        return;
      }
      this.selectNumber = arr.length;
      this.rockerVisible = true;
    },
    rockerClose() {
      this.rockerVisible = false;
      this.nowNumber = 0;
    },
    sumbitRocker() {
      let frame = [0xa5, 0x06, 0xbb, 0x77, 0x88];
      const checksum = frame.reduce((sum, val) => sum + val, 0) & 0xff;
      frame.push(checksum);
      const sendHex = frame
        .map((val) => val.toString(16).padStart(2, "0"))
        .join("");
      console.log("发送 Hex：", sendHex);
      const buffer = new Uint8Array(frame).buffer;
      this.devices.forEach((deviceObj, index) => {
        if (deviceObj.connected && deviceObj.isSeleted && !deviceObj.passed) {
          this.sendDataToDevice(deviceObj, buffer, "rockerClose");
        } else {
          console.warn(
            `❌ 设备 ${deviceObj.device.name || index} 未连接，跳过`
          );
        }
      });
    },
    // 马达
    motorOpen() {
      const arr = this.devices.filter((el) => el.isSeleted);
      if (arr.length === 0) {
        this.$message.error("请选择需要测试的手柄");
        return;
      }
      this.selectNumber = arr.length;
      this.motorVisible = true;
    },
    motorClose() {
      this.motorVisible = false;
      this.nowNumber = 0;
    },
    sumbitMotor() {
      let frame = [0xa5, 0x06, 0xb6, this.power, this.time];
      const checksum = frame.reduce((sum, val) => sum + val, 0) & 0xff;
      frame.push(checksum);
      const sendHex = frame
        .map((val) => val.toString(16).padStart(2, "0"))
        .join("");
      console.log("发送 Hex：", sendHex);
      const buffer = new Uint8Array(frame).buffer;
      this.devices.forEach((deviceObj, index) => {
        if (deviceObj.connected && deviceObj.isSeleted) {
          this.sendDataToDevice(deviceObj, buffer, "motorClose");
        } else {
          console.warn(
            `❌ 设备 ${deviceObj.device.name || index} 未连接，跳过`
          );
        }
      });
    },

    async sendDataToDevice(deviceObj, buffer, fn) {
      try {
        const service = await deviceObj.server.getPrimaryService(
          this.serviceUUID
        );
        const writeChar = await service.getCharacteristic(
          "0000ffe1-0000-1000-8000-00805f9b34fb"
        );
        await writeChar.writeValue(buffer);
        console.log(`✅ 向 ${deviceObj.device.name} 发送成功`);
        this.nowNumber++;
        if (this.nowNumber >= this.selectNumber) {
          this[fn]();
        }
      } catch (err) {
        console.error("❌ 发送失败:", err);
        alert("发送失败，请检查设备连接和指令格式");
      }
    },

    hexStringToBuffer(hex) {
      const bytes = new Uint8Array(hex.length / 2);
      for (let i = 0; i < hex.length; i += 2) {
        bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
      }
      return bytes.buffer;
    },
    // 上传合格设备
    async sumbitQualified() {
      if (!this.qualityTestingAccount) {
        this.$message.error("请输入质检员账号");
        return;
      }
      if (!this.qualityTestingCode) {
        this.$message.error("请输入质检码");
        return;
      }
      const passedList = this.devices.filter((el) => el.passed);
      if (passedList.length === 0) {
        this.$message.error("暂无检验到合格设备");
        return;
      }
      if (this.uploading) return; // 防止重复提交
      this.uploading = true;
      const snCodeList = passedList.map((el) => {
        return {
          deviceName: el.device.name,
          snCode: el.device.id,
        };
      });
      const params = {
        qualityTestingAccount: this.qualityTestingAccount,
        qualityTestingCode: this.qualityTestingCode,
        snCodeList,
      };
      // console.log(params);
      try {
        // 异步操作
        await addCheckDevice(params);
        this.$message.success("合格设备已上传成功！");
      } catch (error) {
        const match = error.match(/([A-Z]+\d+_[a-zA-Z0-9]+)/);

        if (match) {
          const msg = error.replace("Error: ", "");
          this.$confirm(msg + ",即将为您移除这个设备", "提示", {
            confirmButtonText: "确定",
            showCancelButton: false,
            type: "warning",
            showClose: false,
          })
            .then(async () => {
              const deviceName = match[1];
              console.log("提取到设备名:", deviceName);
              // 查找设备对象
              const index = this.devices.findIndex(
                (d) => d.device.name === deviceName
              );

              if (index !== -1) {
                const deviceObj = this.devices[index];
                deviceObj.manuallyDisconnect = true;
                // 尝试断开蓝牙连接
                try {
                  if (
                    deviceObj.device &&
                    deviceObj.device.gatt &&
                    deviceObj.device.gatt.connected
                  ) {
                    await deviceObj.device.gatt.disconnect();
                    console.log(`🔌 已断开蓝牙连接：${deviceName}`);
                  }
                } catch (disconnectErr) {
                  console.warn(`⚠️ 断开 ${deviceName} 时出错：`, disconnectErr);
                }

                // 移除设备
                this.devices.splice(index, 1);
                this.$message.warning(
                  `设备 ${deviceName} 已移除（重复质检或失败）`
                );
              } else {
                this.$message.warning(`未在设备列表中找到 ${deviceName}`);
              }
            })
            .catch(() => {});
        }
      } finally {
        this.uploading = false;
      }
    },
  },
};
</script>

<style scoped lang="scss">
.bluetooth {
  padding: 20px;
  width: 100vw;
  min-height: 100vh;
  box-sizing: border-box;
  .overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(0, 0, 0, 0.4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 999;
  }
  .loading-box {
    background: #fff;
    padding: 20px 30px;
    border-radius: 10px;
    font-size: 16px;
    font-weight: bold;
  }
  .handle-box {
    padding-bottom: 22px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    .btn-box {
      display: flex;
      align-items: center;
      button {
        padding: 10px 20px;
        font-size: 18px;
        border-radius: 10px;
        margin-right: 10px;
        color: #ffffff;
        background: #323232;
        border: none;
        cursor: pointer;
        font-weight: bold;
      }
    }
    ::v-deep .input-box {
      flex: 1;
      width: 0;
      display: flex;
      align-items: center;
      justify-content: center;
      .el-input {
        width: 300px;
        font-size: 22px;
        margin-right: 20px;
        .el-input__inner {
          height: 52px;
          line-height: 52px;
          padding-left: 36px;
          box-sizing: border-box;
        }
        .el-input__inner:focus {
          border-color: #ff5d2f;
        }
        .el-input__icon {
          font-size: 26px;
          line-height: 50px;
        }
      }
    }
    .seleted {
      display: flex;
      // align-items: center;
      align-items: center;
      font-size: 24px;
      font-weight: bold;
      margin-right: 10px;
      p {
        width: 30px;
        height: 30px;
        margin-right: 10px;
        img {
          width: 100%;
          height: 100%;
        }
      }
    }
    .operate-box {
      display: flex;
      align-items: center;
    }
  }
  ::v-deep .el-divider {
    .el-divider__text {
      font-size: 18px;
    }
  }
  .device-block {
    display: flex;
    flex-wrap: wrap;
    .box-card {
      width: calc(33% - 40px);
      margin: 0 40px 30px 0;
      border-radius: 10px;
      position: relative;
      overflow: visible;
      .remove-box {
        position: absolute;
        top: -10px;
        right: -10px;
        ::v-deep .el-icon-remove {
          font-size: 32px;
          color: red;
          font-weight: bold;
        }
      }
      .clearfix {
        display: flex;
        align-items: center;
        justify-content: space-between;
        .seleted {
          display: flex;
          // align-items: center;
          cursor: pointer;
          p {
            width: 30px;
            height: 30px;
            margin-right: 10px;
            img {
              width: 100%;
              height: 100%;
            }
          }
          > div {
            display: flex;
            align-items: center;
          }
          span {
            font-weight: bold;
            font-size: 24px;
            display: block;
          }
          .status {
            font-weight: 500;
            font-size: 24px;
            margin-left: 10px;
          }
        }
        .blue-btn {
          margin-left: 8px;
          cursor: pointer;
        }
      }
      .battery-box {
        display: flex;
        align-items: center;
        justify-content: flex-end;
        margin: 10px 0;

        /* 电池外壳样式 */
        .battery-shell {
          width: 60px;
          height: 26px;
          border: 2px solid #959595;
          border-radius: 4px;
          background-color: #f9f9f9;
          position: relative;
          overflow: hidden;
          transition: border-color 0.3s;

          /* 电量填充条 */
          .battery-level {
            height: 100%;
            border-radius: 4px 0 0 4px;
            transition: width 0.4s ease;
          }
        }

        /* 电池头 */
        .battery-head {
          width: 8px;
          height: 16px;
          background-color: #999;
          border-radius: 4px;
          margin-left: -4px;
          margin-right: 8px;
        }

        .battery-text {
          display: block;
          font-size: 20px;
          font-weight: bold;
        }
      }
      .top {
        display: flex;
        align-items: center;
        justify-content: space-between;
        box-sizing: border-box;
        padding: 0 40px;
        > div {
          display: flex;
          align-items: center;

          div {
            display: flex;
            align-items: center;
            justify-content: center;
            flex-direction: column;
            font-weight: bold;
            font-size: 26px;
            color: #ff5d2f;
            line-height: 36px;
          }

          > div:first-child {
            margin-right: 14px;
          }

          .btn {
            width: 58px;
            height: 16px;
            background: #323232;
            border-radius: 10px 10px 0px 0px;
            border: 2px solid #d8d8d8;
          }
        }
      }
      .center {
        background: #ffffff;
        box-shadow: 0px 5px 9px 0px rgba(198, 198, 198, 0.5);
        border-radius: 20px;
        padding: 0 20px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        box-sizing: border-box;
        width: 100%;

        .rocker,
        .button {
          display: flex;
          flex-direction: column;
          align-items: center;

          .circle {
            background: #323232;
            border: 2px solid #d8d8d8;
            width: 22px;
            height: 22px;
            border-radius: 100%;
          }

          .circle-btn {
            font-weight: bold;
            font-size: 26px;
            color: #ff5d2f;
            line-height: 36px;
            display: flex;
            flex-direction: column;
            align-items: center;

            div {
              background: #323232;
              border: 2px solid #d8d8d8;
              width: 22px;
              height: 22px;
              border-radius: 100%;
            }
          }

          .rocker-btn {
            position: relative;
            margin: 4px;

            div {
              position: absolute;
              top: 50%;
              left: 50%;
              margin-top: -11px;
              margin-left: -11px;
            }
          }

          img {
            width: 80px;
            height: 80px;
          }

          .item-center {
            display: flex;
            align-items: center;

            .circle-btn {
              display: flex;
              align-items: center;
              flex-direction: row;
            }
          }
        }
        .gyroscope {
          display: flex;
          flex-direction: column;
          align-items: center;
          > div {
            padding: 20px 0;
          }

          .key-check-status {
            margin-top: 20px;
            text-align: center;
          }

          /* 🔄 三个点闪烁动画 */
          .loading-dots {
            .dot {
              display: inline-block;
              width: 8px;
              height: 8px;
              margin: 0 4px;
              background-color: #ff7300;
              border-radius: 50%;
              animation: blink 1.4s infinite ease-in-out both;
            }
            .dot:nth-child(1) {
              animation-delay: -0.32s;
            }
            .dot:nth-child(2) {
              animation-delay: -0.16s;
            }
            p {
              margin-top: 10px;
              font-size: 18px;
              font-weight: bold;
            }
          }
          @keyframes blink {
            0%,
            80%,
            100% {
              opacity: 0;
              transform: scale(0.8);
            }
            40% {
              opacity: 1;
              transform: scale(1.2);
            }
          }

          /* ✅ 打勾出现动画 */
          .check-success {
            animation: fadeIn 0.8s ease forwards;
            display: flex;
            align-items: center;
            p {
              font-size: 18px;
              font-weight: bold;
              margin-left: 10px;
            }
            .check-icon {
              font-size: 32px;
              color: #4caf50;
              font-weight: bold;
              margin-bottom: 5px;
              animation: scaleIn 0.4s ease;
            }
            @keyframes fadeIn {
              from {
                opacity: 0;
                transform: translateY(10px);
              }
              to {
                opacity: 1;
                transform: translateY(0);
              }
            }
            @keyframes scaleIn {
              from {
                transform: scale(0);
                opacity: 0;
              }
              to {
                transform: scale(1);
                opacity: 1;
              }
            }
          }
        }
      }
      .calibration-box {
        display: flex;
        align-items: center;
        justify-content: space-around;
      }
      .calibration {
        display: flex;
        h2 {
          font-weight: bold;
          font-size: 20px;
        }
        div {
          display: flex;
          align-items: center;
          p {
            width: 22px;
            height: 22px;
            margin: 0 8px;
            img {
              width: 100%;
              height: 100%;
            }
          }
          span {
            font-size: 20px;
          }
        }
      }
    }
    .box-card:hover {
      transform: translateY(-4px) scale(1.03);
      box-shadow: 0 0 30px rgba(26, 40, 40, 0.4);
    }
    .blink-error {
      animation: softBlink 3s infinite ease-in-out;
      transition: background-color 0.5s ease;
    }
    @keyframes softBlink {
      0% {
        background-color: rgba(250, 250, 250, 0.1);
      }
      50% {
        background-color: rgb(249, 72, 72);
      }
      100% {
        background-color: rgba(250, 250, 250, 0.1);
      }
    }
    .device-success {
      background-color: rgba(46, 251, 148, 0.1);
    }
  }
  .sensor-box {
    text-align: center;
    font-weight: bold;
    font-size: 24px;
    color: #323232;
  }
  .rocker-box {
    p {
      line-height: 36px;
      font-weight: bold;
      font-size: 22px;
      color: #323232;
    }
  }
  ::v-deep .motor-box {
    > div:first-child {
      margin-bottom: 30px;
    }
    > div {
      > span {
        display: block;
        font-size: 20px;
        font-weight: 600;
        margin-bottom: 16px;
      }
      > div {
        display: flex;
        align-items: center;
        > div {
          flex: 1;
          width: 0;
          .el-slider__bar {
            background: #ff5d2f;
          }
          .el-slider__button {
            border: 2px solid #ff5d2f;
            background: #ff5d2f;
            width: 20px;
            height: 20px;
          }
        }
        > p {
          font-size: 20px;
          font-weight: bold;
          color: #333333;
          margin-left: 20px;
        }
      }
    }
  }
}
@keyframes gradientMove {
  0% {
    background-position: 0% 50%;
  }
  50% {
    background-position: 100% 50%;
  }
  100% {
    background-position: 0% 50%;
  }
}
</style>
